/*
 */
package org.actorsguildframework;

import java.util.Arrays;
import java.util.Date;

import junit.framework.Assert;

import org.actorsguildframework.annotations.Initializer;
import org.actorsguildframework.annotations.Message;
import org.actorsguildframework.annotations.Shared;
import org.junit.Test;

/**
 */
public class VariousActorTest {
	
	public static class InitializingActor extends Actor {
		int a;
		@Initializer
		public AsyncResult<InitializingActor> init(int a) {
			Assert.assertNotNull(getAgent());
			this.a = a;
			return result(this);
		}
		
		@Message
		public AsyncResult<Integer> getA() throws Exception {
			Assert.assertNotNull(getAgent());
			return result(a);
		}
	}

	public static class SomeSerializableObject implements java.io.Serializable{
		int a = 2;
	}

	public static class SomeImmutableObject implements Immutable {
		final int a = 3;
	}

	public static interface OpInterface {
		public AsyncResult<Integer> op(int a, int b);
	}
	
	public static class NonInitializingActor extends Actor implements OpInterface {
		@Message
		public AsyncResult<Integer> op(int a, int b) {
			Assert.assertNotNull(getAgent());
			return result(a+b);
		}
		
		@Message
		public AsyncResult<Void> throwing() throws Exception {
			throw new Exception("foo");
		}
		
		@Message
		public AsyncResult<SomeSerializableObject> opValueTypes(int a, String s, SomeSerializableObject sso, SomeImmutableObject sio) {
			Assert.assertEquals(5, a);
			if (s != null)
				Assert.assertEquals("foobar", s);
			if (sso != null)
				Assert.assertEquals(2, sso.a);
			if (sio != null)
				Assert.assertEquals(3, sio.a);
			return result(sso);
		}
		
		@Message
		public AsyncResult<SomeImmutableObject> opReturnImmutable(SomeImmutableObject sio) {
			return result(sio);
		}
		
		@Message
		public AsyncResult<InheritingActor2> opReferenceTypes(InheritingActor1 inaa, OpInterface iwaa) {
			Assert.assertEquals(5, inaa.op(8, 3).get().intValue());
			Assert.assertEquals(24, iwaa.op(8, 3).get().intValue());
			return result((InheritingActor2) iwaa);
		}
		
		@Message
		public AsyncResult<String> opNullReturn() {
			return result(null);
		}
		
		@Message
		public AsyncResult<String[]> opArrays(String[] a, int[] b, Date[] c, String[] d, int bLen, int cLen) {
			Assert.assertEquals(bLen, b.length);
			Assert.assertEquals(cLen, c.length);
			return result(a);
		}
		
		@Message
		public AsyncResult<Void> sharedArgument(@Shared java.io.InputStream a) {
			Assert.assertTrue(a instanceof java.io.InputStream);
			Assert.assertFalse(a instanceof java.io.Serializable);
			Assert.assertNotNull(a);
			return noResult();
		}
	}

	public static class InheritingActor1 extends NonInitializingActor {
		@Message
		@Override
		public AsyncResult<Integer> op(int a, int b) {
			Assert.assertNotNull(getAgent());
			return result(a-b);
		}
	}

	public static class InheritingActor2 extends NonInitializingActor {
		@Message
		@Override
		public AsyncResult<Integer> op(int a, int b) {
			Assert.assertNotNull(getAgent());
			return result(a*b);
		}
	}
	
	public static class ContainerA {
		public static class IdenticalActor extends Actor {
			@Message
			public AsyncResult<Integer> test() {
				return result(1);
			}
		}
	}

	public static class ContainerB {
		public static class IdenticalActor extends Actor {
			@Message
			public AsyncResult<Integer> test() {
				return result(2);
			}
		}
	}
	
	

	
	@Test
	public void testSimpleActorAndMessage() throws Exception {
		DefaultAgent a = new DefaultAgent();
try {
		NonInitializingActor ac = a.create(NonInitializingActor.class);
		
}catch(Throwable t){t.printStackTrace();}

		//NonInitializingActor ac = a.create(NonInitializingActor.class);
		//Assert.assertEquals(5, ac.op(3, 2).get().intValue());
	}

	@Test
	public void testSimpleActorThrowing() throws Exception {
		DefaultAgent a = new DefaultAgent();
		NonInitializingActor ac = a.create(NonInitializingActor.class);
		AsyncResult<Void> r = ac.throwing();
		
		try {
			r.get();
		}
		catch (WrappedException e) {
			Assert.assertTrue(e.isWrapping(Exception.class));
			Assert.assertEquals("foo", e.getCause().getMessage());
			return;
		}
		Assert.fail();
	}

	@Test
	public void testSimpleActorInheriting() {
		DefaultAgent a = new DefaultAgent();
		InheritingActor2 ac = a.create(InheritingActor2.class);
		Assert.assertEquals(6, ac.op(3, 2).get().intValue());
	}

	@Test
	public void testInitialization() throws Exception {
		DefaultAgent a = new DefaultAgent();
		InitializingActor ac = a.create(InitializingActor.class).init(5).get();
		Assert.assertEquals(5, ac.getA().get().intValue());
	}
	
	@Test
	public void testValueTypes() {
		DefaultAgent a = new DefaultAgent();
		NonInitializingActor ac = a.create(NonInitializingActor.class);
		SomeSerializableObject sso = new SomeSerializableObject();
		SomeSerializableObject r = ac.opValueTypes(5, "foobar", sso, new SomeImmutableObject()).get();
		Assert.assertNotSame(sso, r);
		Assert.assertEquals(2, r.a);
	}

	@Test
	public void testValueTypesNull() {
		DefaultAgent a = new DefaultAgent();
		NonInitializingActor ac = a.create(NonInitializingActor.class);
		SomeSerializableObject r = ac.opValueTypes(5, null, null, null).get();
		Assert.assertNull(r);
	}
	
	@Test
	public void testImmutable() {
		DefaultAgent a = new DefaultAgent();
		NonInitializingActor ac = a.create(NonInitializingActor.class);
		SomeImmutableObject sio = new SomeImmutableObject();
		SomeImmutableObject r = ac.opReturnImmutable(sio).get();
		Assert.assertSame(sio, r);
	}
	
	@Test
	public void testReferenceTypes() {
		DefaultAgent a = new DefaultAgent();
		InheritingActor1 inaa = a.create(InheritingActor1.class);
		InheritingActor2 iwaa = a.create(InheritingActor2.class);
		
		NonInitializingActor ac = a.create(NonInitializingActor.class);
		InheritingActor2 r = ac.opReferenceTypes(inaa, iwaa).get();
		Assert.assertSame(iwaa, r);
		Assert.assertFalse(ac.opReferenceTypes(inaa, iwaa) instanceof ImmediateResult);
	}

	@Test
	public void testIdenticallyNamedActors() {
		DefaultAgent a = new DefaultAgent();
		ContainerA.IdenticalActor ia = a.create(ContainerA.IdenticalActor.class);
		ContainerB.IdenticalActor ib = a.create(ContainerB.IdenticalActor.class);
		Assert.assertEquals(1, ia.test().get().intValue());
		Assert.assertEquals(2, ib.test().get().intValue());
	}
	
	@Test
	public void testNullReturningMessage() {
		DefaultAgent a = new DefaultAgent();
		Assert.assertNull(a.create(NonInitializingActor.class).opNullReturn().get());
	}
	
	@Test
	public void testArrays() {
		DefaultAgent da = new DefaultAgent();
		NonInitializingActor nia = da.create(NonInitializingActor.class);
		
		String[] aIn = {"haha", "abc", null, "x", ""};
		String[] aOut = nia.opArrays(aIn, new int[] {1, 2, 3}, new Date[] {new Date(2)}, null, 3, 1).get();

		Assert.assertTrue(Arrays.deepEquals(aIn, aOut));
	}
	
	@Test
	public void testSharedArgument() {
		DefaultAgent da = new DefaultAgent();
		NonInitializingActor nia = da.create(NonInitializingActor.class);

		nia.sharedArgument(System.in).get();
	}
}
